package com.zhuangjie.security.helper.config;

import com.zhuangjie.security.annotations.DefineModuleNameForSecurityFramework;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.Collection;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@ConfigurationProperties(prefix = "jwt-helper.config")
@Data
@Slf4j
public class JwtHelperConfig {


    public TimeUnit timeUnit = TimeUnit.DAYS;

    public Integer accessTimeoutExpireTime = 7;
    public Integer refreshTimeoutExpireTime = 30;

    public String moduleName;

    public String loginStateKey;

    /**
     * 从请求头中的哪项获取token
     */
    public String authorizationHeader = "Authorization";

    /**
     * 创建的token是否需要进行状态控制
     */
    public Boolean enableStateControl = true;

    /**
     * 如果jwtHelperProperties中没有配置moduleName使用项目的名称
     */
    @Autowired(required = false)
    private void init(Environment environment, RedisTemplate redisTemplate, ApplicationContext applicationContext) {
        if (this.moduleName == null)
            this.moduleName = getModuleNameFromAnnotations(applicationContext)
                    .orElse(Optional.ofNullable(environment.getProperty("spring.application.name"))
                            .orElse("UndefinedModules" + System.currentTimeMillis()));
        // 初始化loginStateKey
        this.loginStateKey = loginStateKeyValueOf(moduleName);
        // 如果开启了状态控制，还需要一个条件，那就是存在redis环境
        if (this.enableStateControl) this.enableStateControl = redisTemplate != null;
    }


    /**
     * 解析声明的注解（DefineModuleNameForSecurity）获取模块名称
     *
     * @param applicationContext 应用程序上下文
     * @return {@link Optional}<{@link String}>
     */
    public static Optional<String> getModuleNameFromAnnotations(ApplicationContext applicationContext) {
        Set<String> moduleNames = new HashSet<>();
        Collection<Object> targetBeans = applicationContext.getBeansWithAnnotation(DefineModuleNameForSecurityFramework.class).values();
        for (Object targetBean : targetBeans) {
            Class<?> targetClass = AopProxyUtils.ultimateTargetClass(targetBean);
            DefineModuleNameForSecurityFramework defineModuleNameForSecurityFramework = targetClass.getAnnotation(DefineModuleNameForSecurityFramework.class);
            if (defineModuleNameForSecurityFramework == null) continue;
            moduleNames.add( defineModuleNameForSecurityFramework.value() );
        }
        if (moduleNames.isEmpty()) {
            log.warn("安全框架: 未为模块声明模块名称！但仍然会为你声明一个模块名称，但这是没意义，但必要的事情！");
            return Optional.empty();
        } else if (moduleNames.size() > 1) {
            throw new RuntimeException("安全框架：你在此项目多处使用DefineModuleNameForSecurity声明模块名！");
        }
        return moduleNames.stream().findFirst();
    }

    public static String loginStateKeyValueOf(String moduleName) {
        return  "LoginState::"+moduleName;
    }
}
